package com.adee.java;

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ThreadLocalRandom;

public class Test013_ThreadLocalRandom {
    public static void main(String[] args) throws InterruptedException {
        test0();
        // test1();
//        test2();
    }

    /**
     * https://blog.csdn.net/qq_18244417/article/details/114275337
     *
     * Random类创建随机数，是伪随机，random.nextXXX()方法是线程安全的。
     * 不同线程使用同一个random对象生成的随机数不重复。但是在高并发环境下，
     * Random内部使用AtomicLong，由于大量CAS操作失败造成不断spin，从而
     * 导致cpu开销比较大，而且吞吐量也会下降。
     * 现在发现问题就是大量的并发竞争，使得 CAS 失败，对于竞争问题的优化策略在前文 AtomicLong 和 LongAdder 时，
     * 也谈到了。锁的极限优化是 Free Lock，如 ThreadLocal 方式。
     * 在 JDK 1.7 中由并发大神引入了 ThreadLocalRandom 来解决 Random 的大并发问题，以下两者的测试结果比较。
     * 可以看出 Random 随着竞争越来越激烈，然后耗时越来越多，说明吞吐量将成倍下降。然而 ThreadLocalRandom
     * 随着线程数的增加，基本没有变化。所以在大并发的情况下，随机的选择，可以考虑 ThreadLocalRandom 提升性能，
     * 也是性能优化之道的一步。
     *
     * 三. 总结
     * Random 是 Java 中提供的随机数生成器工具类，但是在大并发的情况下由于其随机种子的竞争会导致吞吐量下降，从而引入
     * ThreadLocalRandom。它将竞争点隔离到每个线程中，从而消除了大并发情况下竞争问题，提升了性能。
     * 从两者的设计上，可以看出在处理并发优化时的优秀设计思想，对于竞争问题，可以将将竞争点隔离，如使用 ThreadLocal 实现。
     *
     * 并发竞争的整体优化思路，还是像前文中总结的一样：
     * lock -> cas + volatile -> free lock
     * 只会 free lock 的设计方式就是避免竞争，将竞争点隔离到线程中，从而解决竞争。
     *
     */
    public static void test0() throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(20);
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            System.out.println("-- " + random.nextInt());
            latch.countDown();
        }
        // 另一个线程中执行
        ForkJoinPool.commonPool().execute(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("   " + random.nextInt());
                latch.countDown();
            }
        });
        latch.await();
    }

    /*
    * ThreadLocalRandom错误用法
    *
    *   t0: -1667209487
        t7: -1667209487
        t8: -1667209487
        t5: -1667209487
        t9: -1667209487
        t3: -1667209487
        t4: -1667209487
        t1: -1667209487
        t2: -1667209487
        t6: -1667209487
    * 错误原因：ThreadLocalRandom.current()方法必须在每个线程中调用一次，目的是初始化种子seed。
    * */
    public static void test1() {
        ThreadLocalRandom random = ThreadLocalRandom.current();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + ": " + random.nextInt());
            }, "t"+i).start();
        }
    }

    /*
    * 正确用法
    *   t4: 724
        t2: 633
        t3: 115
        t7: 939
        t1: 747
        t8: 499
        t9: 739
        t5: 536
        t6: 732
        t0: 755
    * */
    public static void test2() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + ": " + ThreadLocalRandom.current().nextInt(1000));
            }, "t"+i).start();
        }
    }
}
